#[derive(Debug, Clone, Copy)]
pub enum PageKind {
    Small,  // small blocks go into 64KiB pages inside a segment
    Medium, // medium blocks go into 512KiB pages inside a segment
    Large,  // larger blocks go into a single page spanning a whole segment
}

pub const MI_SMALL_PAGE_SHIFT: usize = 16; // 64KiB
pub const MI_MEDIUM_PAGE_SHIFT: usize = 19; // 512KiB
pub const MI_LARGE_PAGE_SHIFT: usize = 22; // 4MiB

pub const MI_SMALL_PAGE_SIZE: usize = 1 << MI_SMALL_PAGE_SHIFT;
pub const MI_MEDIUM_PAGE_SIZE: usize = 1 << MI_MEDIUM_PAGE_SHIFT;
pub const MI_LARGE_PAGE_SIZE: usize = 1 << MI_LARGE_PAGE_SHIFT;

// Small sizes: 8, 16, 24, 32, 40, 48, 56, 64, ..., 8192
pub const SMALL_SIZES: &[usize] = &[
    8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128, 144, 160, 176, 192, 208,
    224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 832, 896, 960, 1024,
    1152, 1280, 1408, 1536, 1664, 1792, 1920, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096,
    4608, 5120, 5632, 6144, 6656, 7168, 7680, 8192,
];

// Medium sizes start after small sizes
pub const MEDIUM_SIZES: &[usize] = &[
    8192 * 2,  // 16KB
    8192 * 4,  // 32KB
    8192 * 8,  // 64KB
    8192 * 16, // 128KB
    8192 * 32, // 256KB
    8192 * 64, // 512KB
];

#[derive(Debug, Clone, Copy)]
pub struct SizeClass {
    pub block_size: usize,
    pub page_kind: PageKind,
}

impl SizeClass {
    pub fn new(size: usize) -> Self {
        debug_log!("\n📏 Size class calculation for {} bytes:", size);

        let (block_size, page_kind) = if size <= 8192 {
            // Find the smallest small size that fits
            let idx = SMALL_SIZES
                .iter()
                .position(|&s| s >= size)
                .unwrap_or(SMALL_SIZES.len() - 1);
            debug_log!("  ├─ Small allocation (bin {})", idx);
            debug_log!("  └─ Block size: {} bytes", SMALL_SIZES[idx]);
            (SMALL_SIZES[idx], PageKind::Small)
        } else if size <= MI_MEDIUM_PAGE_SIZE {
            // Find the smallest medium size that fits
            let idx = MEDIUM_SIZES
                .iter()
                .position(|&s| s >= size)
                .unwrap_or(MEDIUM_SIZES.len() - 1);
            debug_log!("  ├─ Medium allocation (bin {})", idx);
            debug_log!("  └─ Block size: {} bytes", MEDIUM_SIZES[idx]);
            (MEDIUM_SIZES[idx], PageKind::Medium)
        } else {
            // Large pages get their own segment
            debug_log!("  └─ Large allocation (exact size)");
            (size, PageKind::Large)
        };

        SizeClass {
            block_size,
            page_kind,
        }
    }

    pub fn page_size(&self) -> usize {
        match self.page_kind {
            PageKind::Small => MI_SMALL_PAGE_SIZE,
            PageKind::Medium => MI_MEDIUM_PAGE_SIZE,
            PageKind::Large => MI_LARGE_PAGE_SIZE,
        }
    }
}

impl std::fmt::Display for SizeClass {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let size_info = match self.page_kind {
            PageKind::Small => {
                let idx = SMALL_SIZES
                    .iter()
                    .position(|&s| s == self.block_size)
                    .unwrap_or(0);
                format!("Small[bin={}]", idx)
            }
            PageKind::Medium => {
                let idx = MEDIUM_SIZES
                    .iter()
                    .position(|&s| s == self.block_size)
                    .unwrap_or(0);
                format!("Medium[bin={}]", idx)
            }
            PageKind::Large => "Large".to_string(),
        };

        write!(f, "{} {{block_size={}b}}", size_info, self.block_size)
    }
}

impl std::fmt::Display for PageKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            PageKind::Small => write!(f, "Small"),
            PageKind::Medium => write!(f, "Medium"),
            PageKind::Large => write!(f, "Large"),
        }
    }
}
